using System.CodeDom;
using System;
using System.Collections.Generic;
using System.Text;

namespace GenCode
{
    class CodeProvider
    {
        /// <summary>
        /// 

        /// 
        /// </summary>
        private static List<String> declaredVar = new List<String>();
        private static List<String> declaredUserDataType = new List<String>();
        private static List<String> declaredNamespace = new List<String>();

        /// <summary>
        /// 

        /// 
        /// </summary>
        public static void clearAllUserDataDeclaration()
        {
            declaredNamespace.Clear();
            declaredUserDataType.Clear();
            declaredVar.Clear();
        }

        /// <summary>
        /// 
        /// T?o namespace
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static CodeNamespace CreateNamespace(String name)
        {
            String nsName = name;
            int count = 0;
            do
            {
                nsName = name + (count == 0 ? "" : count.ToString());
            } while (declaredNamespace.Contains(nsName));
            declaredUserDataType.Add(nsName);
            return new CodeNamespace(nsName);
        }

        /// <summary>
        ///     
        /// T?o class
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static CodeTypeDeclaration CreateClass(String name)
        {
            String className = name;
            int count = 0;
            do
            {
                className = name + (count == 0 ? "" : count.ToString());
            } while (declaredUserDataType.Contains(className));
            declaredUserDataType.Add(className);
            return new CodeTypeDeclaration(className);
        }

        public static CodeTypeReference GetType(String type)
        {
            String typeName = (String)Misc.Types[type.Replace("*", "")];
            int d = type.IndexOf('*') != -1 ? type.LastIndexOf('*') - type.IndexOf('*') + 1 : 0;
            for (int i = 0; i < d; i++)
                typeName += "[]";
            return new CodeTypeReference(typeName);
        }

        public static CodePrimitiveExpression GetDefaultErrorValue(String type)
        {
            int d = type.IndexOf('*') != -1 ? type.LastIndexOf('*') - type.IndexOf('*') + 1 : 0;
            if (d > 0)
            {
                return new CodePrimitiveExpression(null);
            }
            return new CodePrimitiveExpression(Misc.Error[type]);
        }

        public static CodeMemberMethod CreateMethod(String name)
        {
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = name;
            method.Attributes = MemberAttributes.Public;
            return method;
        }

        public static CodeParameterDeclarationExpression CreateParameter(String name, String type)
        {
            String parName = name;
            int count = 0;
            do
            {
                parName = name + (count == 0 ? "" : count.ToString());
                count++;
            } while (declaredVar.Contains(parName));
            declaredVar.Add(parName);
            return new CodeParameterDeclarationExpression(GetType(type), parName);
        }

        public static CodeVariableDeclarationStatement CreateVariable(String name, String type)
        {
            String varName = name;
            int count = 0;
            do
            {
                varName = name + (count == 0 ? "" : count.ToString());

                count++;

            } while (declaredVar.Contains(varName));
            declaredVar.Add(varName);
            return new CodeVariableDeclarationStatement(GetType(type), varName);
        }

        public static CodeVariableDeclarationStatement CreateArray(String name,
                                                                    String type,
                                                                    CodeExpression size)
        {
            CodeVariableDeclarationStatement arr = CreateVariable(name, type + "[]");
            arr.InitExpression = new CodeArrayCreateExpression(type, size);
            return arr;
        }

        public static CodeStatementCollection CreateArray(String name,
                                                          String type,
                                                          CodeExpression[] sizeLs)
        {
            CodeStatementCollection stmtClx = new CodeStatementCollection();
            String suffix = "";
            for (int i = 0; i < sizeLs.Length - 1; i++) suffix += "[]";
            CodeVariableDeclarationStatement MArr = CreateArray(name, type, sizeLs[0]);
            stmtClx.Add(MArr);
            for (int i = 0; i < sizeLs.Length - 1; i++)
            {
                CodeVariableDeclarationStatement init = CreateVariable("i", "int");
                init.InitExpression = CreateValue(0);
                CodeBinaryOperatorExpression test = CreateOperatorExp(GetVariable(init.Name), "<", sizeLs[i]);
                CodeAssignStatement increment = new CodeAssignStatement(GetVariable(init.Name),
                                                    CreateOperatorExp(GetVariable(init.Name), "+", CreateValue(1)));
                CodeIterationStatement loop = CreateLoop(init, test, increment);
                String suf = "";
                for (int k = 0; k < sizeLs.Length - (i + 2); k++) suf += "[]";
                CodeAssignStatement arrItem = new CodeAssignStatement(
                    GetArrayIndexer(GetVariable(MArr.Name), GetVariable(init.Name)),
                    new CodeArrayCreateExpression(type + suf, sizeLs[i + 1]));
                loop.Statements.Add(arrItem);
                stmtClx.Add(loop);
            }
            return stmtClx;
        }

        public static CodePrimitiveExpression CreateValue(Object value)
        {
            return new CodePrimitiveExpression(value);
        }

        public static CodeVariableReferenceExpression GetVariable(String name)
        {
            if (declaredVar.Contains(name))
                return new CodeVariableReferenceExpression(name);
            return null;
        }

        public static CodeArrayIndexerExpression GetArrayIndexer(CodeExpression targetArr,
                                                                 CodeExpression[] indexExp)
        {
            CodeArrayIndexerExpression indexer = GetArrayIndexer(targetArr, indexExp[0]);
            for (int i = 1; i < indexExp.Length; i++)
            {
                indexer = GetArrayIndexer(indexer, indexExp[i]);
            }
            return indexer;
        }

        public static CodeArrayIndexerExpression GetArrayIndexer(CodeExpression targetArr,
                                                                 CodeExpression indexExp)
        {
            CodeArrayIndexerExpression indexer = new CodeArrayIndexerExpression(targetArr, indexExp);
            return indexer;
        }

        public static CodeBinaryOperatorExpression CreateOperatorExp(CodeExpression leftExp,
                                                                     String opSign,
                                                                     CodeExpression rightExp)
        {
            if (opSign == "<>")
            {
                return CreateOperatorExp(CreateOperatorExp(leftExp, "=", rightExp), "=", CreateValue(false));
            }
            return new CodeBinaryOperatorExpression(leftExp, (CodeBinaryOperatorType)Misc.Operators[opSign], rightExp);
        }

        public static CodeIterationStatement CreateLoop(CodeStatement initStmt,
                                                        CodeExpression testExp,
                                                        CodeStatement incrementStmt)
        {
            CodeIterationStatement loop = new CodeIterationStatement();
            loop.InitStatement = initStmt;
            loop.TestExpression = testExp;
            loop.IncrementStatement = incrementStmt;
            return loop;
        }

        public static CodeMethodReturnStatement CreateReturnStmt(CodeExpression exp)
        {
            return new CodeMethodReturnStatement(exp);
        }
    }
}
